﻿using System;
using System.Threading;
using DBServer.Selenium.Extenium.Exception;
using DBServer.Selenium.Extenium.Path;
using Selenium;

namespace DBServer.Selenium.Extenium.Fixtures
{
    public class ExtComponentFixture
    {
        protected const long Timeout = 10000;
        protected ISelenium Driver;
        protected IPath Path;

        public ExtComponentFixture(ISelenium driver, IPath path)
        {
            Driver = driver;
            Path = path;

            var eval = "";
            const int maxRetry = 10;
            const int sleepInterval = 500;
            for (var i = 0; i < maxRetry; i++)
            {
                eval = Path.Eval();
                if (eval == "null" || eval == "undefined")
                {
                    Thread.Sleep(sleepInterval);
                }
                else
                {
                    break;
                }
            }
            if (eval == "null" || eval == "undefined")
            {
                throw new ExteniumException("Component not found: " + path.Eval());
            }
            try
            {
                Driver.GetEval(Path.Eval() + ".focus()");
            }
            catch (SeleniumException)
            {
                //se o componente não suporta focus, então continua sem o focus
            }

            
        }

        /// <summary>
        /// Return true if the condition, whilst a period of time, become true. Otherwise return false.
        /// </summary>
        /// <param name="condition"></param>
        /// <returns></returns>
        protected bool Wait(Condition condition)
        {
            var millisAtStart = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
            var millisNow = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
            while (millisNow - millisAtStart <= Timeout)
            {
                if (condition())
                    return true;
                Thread.Sleep(100);
                millisNow = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
            }

            return false;
        }

        public string GetProperty(string propertyName)
        {
            return SafeExec(Path + "." + propertyName);
        }

        protected string SafeExec(string command)
        {
            const int maxRetry = 10;
            const int sleepInterval = 500;

            //Wait until the Dialog becomes invisible, or, the visiblity becomes not true
            var invisible = Wait(() => GetDialogVisibility() != "true");

            if (!invisible)
            {
                throw new ExteniumException("Component is not acessible. A dialog was triggered.");
            }

            for (var i = 0; i < maxRetry; i++)
            {
                try
                {
                    return Driver.GetEval(command);
                }
                catch (SeleniumException)
                {
                    if (i < maxRetry)
                    {
                        Thread.Sleep(sleepInterval);
                    }
                    else
                    {
                        throw;
                    }
                }
            }
            return null;
        }

        private string GetDialogVisibility()
        {
            return Driver.GetEval("window.Ext.MessageBox.isVisible();");
        }

        protected Condition EvalToTrue(string expression)
        {
            return delegate
            {
                var eval = Driver.GetEval(expression);
                return Boolean.Parse(eval);
            };
        }

        protected Condition EvalToFalse(string expression)
        {
            return delegate
            {
                var eval = Driver.GetEval(expression);
                return !Boolean.Parse(eval);
            };
        }

    }

    public delegate Boolean Condition();
}